/*
 * io.c
 *
 *  Created on: Mar 31, 2012
 *      Author: lin
 */
#include "def/defs.h"
#include "io.h"

#include "collection/link_list.h"
#include "zmalloc/zmalloc.h"

int io_fread_unlocked(FILE *fp, size_t size, size_t n, void *buf,
        size_t *readed) {
    assert(fp && buf);

    size_t left = n;
    while (left > 0) {
        const size_t count = fread_unlocked(buf, size, left, fp);
        left -= count;
        if (feof_unlocked(fp)) {
            break;
        } else if (ferror_unlocked(fp) && errno != EINTR) {
            return errno;
        }
    }
    if (readed) {
        *readed = n - left;
    }
    return 0;
}

int io_fwrite_unlocked(FILE *fp, size_t size, size_t n, const void *buf) {
    assert(fp && buf);
    size_t left = n;
    while (left > 0) {
        const size_t count = fwrite_unlocked(buf, size, left, fp);
        if (ferror_unlocked(fp) && errno != EINTR)
            return errno;
        buf = ((char *) buf) + size * count;
        left -= count;
    }
    return 0;
}

int io_pread(int fd, void *buf, size_t n, int64_t off, size_t *readed) {
    assert(off >= 0);

    size_t left = n;
    while (left > 0) {
        const ssize_t count = pread(fd, buf, left, off);
        if (count > 0) {
            buf = ((char *) buf) + count;
            left -= count;
            off += count;
        } else if (count == 0) {
            break;
        } else {
            int err = errno;
            if (err != EINTR) {
                return err;
            }
        }
    }
    if (readed) {
        *readed = n - left;
    }
    return 0;
}

int io_pwrite(int fd, const void *buf, size_t n, int64_t off) {
    assert(off >= 0);

    size_t left = n;
    while (left > 0) {
        const ssize_t count = pwrite(fd, buf, left, off);
        if (count >= 0) {
            buf = ((char *) buf) + count;
            left -= count;
            off += count;
        } else {
            int err = errno;
            if (err != EINTR) {
                return err;
            }
        }
    }
    return 0;
}

int io_read(int fd, void *buf, size_t n, size_t *readed) {
    assert(buf);
    size_t left = n;
    while (left > 0) {
        const ssize_t count = read(fd, buf, left);
        if (count > 0) {
            buf = ((char *) buf) + count;
            left -= count;
        } else if (count == 0) {
            break;
        } else {
            int err = errno;
            if (err != EINTR) {
                return err;
            }
        }
    }

    if (readed) {
        *readed = n - left;
    }
    return 0;
}

int io_write(int fd, const void *buf, size_t n) {
    assert(buf);
    size_t left = n;
    while (left > 0) {
        const ssize_t count = write(fd, buf, left);
        if (count >= 0) {
            buf = ((char *) buf) + count;
            left -= count;
        } else {
            int err = errno;
            if (err != EINTR) {
                return err;
            }
        }
    }
    return 0;
}
int io_file_list_fliter_sysdot(const struct dirent* entry) {
    const char* name = entry->d_name;
    return strcmp(name, ".") && strcmp(name, "..");
}

int io_file_list(const char *dir, char *** list, int *num,
        io_file_list_fliter_proc filter) {
    assert(dir && num);
    DIR* d = opendir(dir);
    if (d == NULL) {
        return errno;
    }

    link_list_t children;
    l_list_reset(&children);

    struct dirent entry;
    struct dirent *entryPtr = NULL;

    int read;
    while (((read = readdir_r(d, &entry, &entryPtr)) == 0) && entryPtr != NULL) {
        if (!filter || filter(&entry)) {
            char *child = zstrdup(entry.d_name);
            l_list_add_tail(&children, child);
        }
    }

    closedir(d);
    int len = l_list_len(&children);
    *num = len;
    if (read == 0 && len > 0) {
        char **rt = (char**) l_list_to_array(&children);
        *list = rt;
    }
    l_list_clear(&children);
    return read;
}

int io_rmdir_r(const char *dir) {
    int no = io_rmdir(dir);
    if (no == 0 || no != EEXIST) {
        return no;
    }

    char ** list;
    int num;
    int no = io_file_list(dir, &list, &num, io_file_list_fliter_sysdot);
    if (no != 0) {
        return no;
    }
    for (int i = 0; i < num; i++) {
        const char *f = *(list + i);
    }
    return 0;
}
